package com.fyfeng.java.bootwrapper;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.fyfeng.java.bootwrapper.conf.Configuration;

public class Wrapper {
	private static final Log log = LogFactory.getLog("WRAPPER");

	private static final String COMMAND_STARTUP = "startup";
	private static final String COMMAND_SHUTDOWN = "shutdown";
	private static final String COMMAND_RUNSERVICE = "runservice";

	private Configuration conf = new Configuration();
	private ServerSocket ssocket;

	private Thread serviceThread;
	private boolean isRunning = false;
	private boolean isShutdown = false;

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		if (null == args || 0 == args.length) {
			System.out.println("No Args.");
			if (log.isErrorEnabled()) {
				log.error("No Args .");
			}
			return;
		}
		if (1 != args.length) {
			if (log.isErrorEnabled()) {
				log.error("Too Much Args .");
			}
			return;
		}

		String command = args[0];

		Wrapper wrapper = new Wrapper();
		if (COMMAND_STARTUP.equals(command)) {
			Thread.currentThread().setName("Wrapper");
			wrapper.addShutdownHook();

			wrapper.startup();
		} else if (COMMAND_SHUTDOWN.equals(command)) {
			wrapper.shutdown();
		}

	}

	private void shutdown() {

		if (log.isInfoEnabled()) {
			log.info("Shutdown Server.");
		}

		this.sendCmd(COMMAND_SHUTDOWN);
	}

	private void startup() {

		final int port = conf.getInt("port");

		if (log.isInfoEnabled()) {
			log.info("Startup Server, Port[" + port + "]");
		}

		try {
			ssocket = ServerSocketFactory.getDefault().createServerSocket(port);
			this.sendCmd(COMMAND_RUNSERVICE);
			while (!isShutdown) {
				Socket socket = ssocket.accept();
				String command = null;
				InputStream input = null;
				try {
					input = socket.getInputStream();
					command = IOUtils.toString(input);
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					if (null != input) {
						IOUtils.closeQuietly(input);
					}
					if (null != socket) {
						IOUtils.closeQuietly(socket);
					}
				}

				if (StringUtils.isEmpty(command)) {
					continue;
				}

				doCommand(command);

			}// end while

			IOUtils.closeQuietly(ssocket);

			try {
				serviceThread.interrupt();
				serviceThread = null;
				this.isRunning = false;
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				System.exit(0);
				if (log.isInfoEnabled()) {
					log.info("Syste Exit.");
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}

	}

	private void doCommand(String command) {
		if (COMMAND_RUNSERVICE.equals(command)) {
			this.doRunnable();
		} else if (COMMAND_SHUTDOWN.endsWith(command)) {
			this.isShutdown = true;
		}
	}

	private void doRunnable() {

		if (this.isRunning) {
			if (log.isWarnEnabled()) {
				log.warn("Service Started .");
			}
			return;
		}

		String className = conf.getString("classname");
		if (log.isInfoEnabled()) {
			log.info("Run Service[" + className + "]");
		}

		try {
			Class<?> clazz = Class.forName(className);
			Runnable runnable = (Runnable) clazz.newInstance();
			serviceThread = new Thread(runnable);
			serviceThread.setName("Service");
			serviceThread.start();
			this.isRunning = true;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	private void sendCmd(String command) {

		if (log.isDebugEnabled()) {
			log.debug("Command[" + command + "]");
		}

		if (StringUtils.isEmpty(command)) {
			return;
		}

		final int port = conf.getInt("port");
		Socket csocket = null;
		OutputStream output = null;

		try {
			csocket = SocketFactory.getDefault().createSocket("localhost", port);
			output = csocket.getOutputStream();
			output.write(command.getBytes());
			output.flush();
		} catch (IOException e) {

			e.printStackTrace();
		} finally {
			if (null != output) {
				IOUtils.closeQuietly(output);
			}
			if (null != csocket) {
				IOUtils.closeQuietly(csocket);
			}
		}

	}

	private void addShutdownHook() {
		java.lang.Runtime.getRuntime().addShutdownHook(new Thread() {

			@Override
			public void run() {
				log.error("#############################################################");
				log.error("Program Stop .");
				log.error("#############################################################");
			}
		});
	}
}
